/*
	Author: Andrew Erridge
*/
#include "gtest/gtest.h"
#include "gmock\gmock.h"
#include "KitMock.h"
#include "Enums.h"
#include "Kit.h"
#include "PartRobotAgent.h"
#include "KitRobotAgentMock.h"
#include "NestAgentMock.h"
#include "Table.h"
#include "GUIPartRobotMock.h"
#include <vector>

/**
 * @class	PartRobotAgentTest
 *
 * @brief	Part robot agent test.
 *
 * @author	Dru
 * @date	11/8/2011
 */

class PartRobotAgentTest : public ::testing::Test {

protected:

	PartRobotAgentTest() : agent("TestPRA"){

	}

	virtual void SetUp() 
	{
		sharedDataTable = new TableInterface("tableInterface");
		agent.setKitRobot( &kitRobotMock );
		agent.setTable( sharedDataTable );
		agent.addNest( &mockNest1 );
		agent.addNest( &mockNest2 );
		agent.addNest( &mockNest3 );
		agent.addNest( &mockNest4 );
		agent.setGUI(&mockGUI);
	}

	PartRobotAgent agent;

	TableInterface *sharedDataTable;
	
	KitRobotAgentMock kitRobotMock;

	NestAgentMock mockNest1;

	NestAgentMock mockNest2;

	NestAgentMock mockNest3;

	NestAgentMock mockNest4;

	GUIPartRobotMock mockGUI;
	

	bool runScheduler() {
		return agent.pickAndExecuteAnAction();
	}

	std::vector<PartRobotAgent::MyKit> getKits()  {
		return agent.myKits;
	}

	std::vector<PartRobotAgent::MyNest> getNests()  {
		return agent.myNests;
	}

	std::vector<PartType> getPartsInHands() {
		return agent.partsInHands;
	}

	PartRobotAgent::TableStatus getTableStatus() {
		return agent.tableStatus;
	}

	std::vector<PartType> getPartTypesNeeded() {
		return agent.partTypesNeeded;
	}

};

TEST_F(PartRobotAgentTest, InitialTest) {
	EXPECT_EQ( getNests().size(), 4 );
	//num of nests = 4
	EXPECT_EQ( getKits().size(), 0 );
	//kits = 0
	EXPECT_EQ( agent.kitRobot, &kitRobotMock );
	//kit robot initialized properly
}

TEST_F(PartRobotAgentTest, SchedulerInitialReturnTest){
	EXPECT_EQ(runScheduler(),false);
}

/*** TESTING MESSAGES ***/

TEST_F( PartRobotAgentTest, msgImStableTest ) {
	agent.msgImStable( &mockNest1 );
	EXPECT_EQ( getNests().at( 0 ).state, PartRobotAgent::NestState::STABLE );
}

TEST_F( PartRobotAgentTest, msgMakeKitTest) {
	KitMock kit = KitMock();
	agent.msgMakeKit( &kit );
	EXPECT_EQ( getKits().size(), 1 );
}

TEST_F( PartRobotAgentTest, msgYouCanGoToTableTest ) {
	agent.msgYouCanGoToTable();
	EXPECT_EQ( agent.tableStatus, PartRobotAgent::TableStatus::ACCESSIBLE );
}

TEST_F( PartRobotAgentTest, msgPartsRetrievedTest ) {
	agent.msgPartRetrieved(NULL, PartType::EMPTY);
	EXPECT_EQ( agent.partRetrievedSema.getValue(), 1 );
}

TEST_F( PartRobotAgentTest, msgPartsDumpedTest ) {
	agent.msgPartsDumped();
	EXPECT_EQ( agent.partsDumpedSema.getValue(), 1 );
}

/*** TESTING SCHEDULER RULES ***/

TEST_F( PartRobotAgentTest, GivenKitAskForParts ) {
	KitMock* kit = new KitMock();
	kit->partTypesNeeded.push_back( PartType::A );
	kit->partTypesNeeded.push_back( PartType::B );
	kit->partTypesNeeded.push_back( PartType::C );
	kit->partTypesNeeded.push_back( PartType::D );

	agent.msgMakeKit( kit );
	
	EXPECT_EQ( runScheduler(), true );
	
	//all 4 nests should be working
	EXPECT_EQ( getNests().at( 0 ).isMakingUsefulPart, true );
	EXPECT_EQ( getNests().at( 1 ).isMakingUsefulPart, true );
	EXPECT_EQ( getNests().at( 2 ).isMakingUsefulPart, true );
	EXPECT_EQ( getNests().at( 3 ).isMakingUsefulPart, true );

	// nest should hold a, b, c, or d
	for ( int i = 0; i < 4; ++i )
	{
		EXPECT_TRUE( (getNests().at( i ).partTypeHeld == PartType::A ||
		 			  getNests().at( i ).partTypeHeld == PartType::B ||
	 				  getNests().at( i ).partTypeHeld == PartType::C ||
	 				  getNests().at( i ).partTypeHeld == PartType::D
	 			    ) );
	}
}

TEST_F( PartRobotAgentTest, NestHasPartINeedTest ) {
	//agent thinks it needs parttype b
	agent.partTypesNeeded.push_back( PartType::B );

	EXPECT_CALL( mockGUI, doPickup( mockNest1.getGUI(), 0 ) ).Times( 1 );

	agent.myNests.at( 0 ).partTypeHeld = PartType::B;

	agent.msgImStable( getNests().at( 0 ).nest );
	
	agent.msgPartRetrieved( 0, PartType::B ); //executing on single thread, must open semaphore
	EXPECT_TRUE( runScheduler() == true );

	EXPECT_TRUE( getNests().at( 0 ).state == PartRobotAgent::NestState::UNSTABLE );
}

TEST_F( PartRobotAgentTest, MyHandsAreFullTest ) {

	KitMock kit = KitMock();
	kit.partTypesNeeded.push_back( PartType::A );
	kit.partTypesNeeded.push_back( PartType::B );
	kit.partTypesNeeded.push_back( PartType::C );
	kit.partTypesNeeded.push_back( PartType::D );
	
	EXPECT_CALL( kitRobotMock, msgCanIGoToTable() ).Times( 1 ); 
	//EXPECT_CALL( mockGUI, doDumpLoad( NULL ) ).Times( 1 );

	agent.msgMakeKit( &kit );
	EXPECT_TRUE( runScheduler() );
	
	qDebug()<< "TABLE STATUS IS: " << agent.tableStatus;
	agent.partsInHands.at( 0 ) = PartType::A;
	agent.partsInHands.at( 1 ) = PartType::B;
	agent.partsInHands.at( 2 ) = PartType::C;
	agent.partsInHands.at( 3 ) = PartType::D;

	EXPECT_TRUE ( agent.handsAreFull() );
	//should ask kitRobot to dump load 
	EXPECT_TRUE( runScheduler() );

	EXPECT_TRUE( agent.tableStatus == PartRobotAgent::TableStatus::PENDING );
	
	agent.msgYouCanGoToTable();
	agent.msgPartsDumped();
}

/*** TESTING SITAUTIONS ***/

TEST_F( PartRobotAgentTest, GivenTwoKitsTest ) {
	KitMock mockKit1;
	KitMock mockKit2;

	EXPECT_CALL( kitRobotMock, msgCanIGoToTable() ).Times( 1 ); 
	//EXPECT_CALL( mockGUI, doDumpLoad( (Kit)mockKit1 ) ).Times( 1 );

	mockKit1.partTypesNeeded.push_back( PartType::A );
	mockKit1.partTypesNeeded.push_back( PartType::B );
	mockKit1.partTypesNeeded.push_back( PartType::C );
	mockKit1.partTypesNeeded.push_back( PartType::D );

	mockKit2.partTypesNeeded.push_back( PartType::E );
	mockKit2.partTypesNeeded.push_back( PartType::F );
	mockKit2.partTypesNeeded.push_back( PartType::G );
	mockKit2.partTypesNeeded.push_back( PartType::H );

	agent.msgMakeKit( &mockKit2 );
	
	EXPECT_TRUE( runScheduler() );

	agent.msgMakeKit( &mockKit1 );

	EXPECT_FALSE( runScheduler() );

	for (int i = 0; i < agent.myNests.size(); ++i ) 
	{
		EXPECT_TRUE( agent.myNests.at( i ).isMakingUsefulPart );
	}

	agent.msgImStable( &mockNest1 );
	agent.msgImStable( &mockNest2 );
	agent.msgImStable( &mockNest3 );
	agent.msgImStable( &mockNest4 );

	agent.msgPartRetrieved( 0, PartType::E );
	EXPECT_TRUE( runScheduler() );

	agent.msgPartRetrieved( 0, PartType::F );
	EXPECT_TRUE( runScheduler() );

	agent.msgPartRetrieved( 0, PartType::G );
	EXPECT_TRUE( runScheduler() );

	agent.msgPartRetrieved( 0, PartType::H );
	EXPECT_TRUE( runScheduler() );

	EXPECT_TRUE( runScheduler() );
	agent.msgYouCanGoToTable();
	
	agent.msgPartsDumped();
	EXPECT_TRUE( runScheduler() );
}